home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / b / b.lha / B / src / bed / demo.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-11-24  |  15.0 KB  |  831 lines

  1. /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1984. */
  2. static char rcsid[] = "$Header: demo.c,v 2.6 85/08/22 16:01:21 timo Exp $";
  3.  
  4. /*
  5.  * B editor -- Editor command processor.
  6.  */
  7.  
  8. #include <ctype.h>
  9.  
  10. #include "b.h"
  11. #include "feat.h"
  12. #include "erro.h"
  13. #include "bobj.h"
  14. #include "node.h"
  15. #include "gram.h"
  16. #include "keys.h"
  17. #include "supr.h"
  18.  
  19. #ifdef BTOP
  20. #include <setjmp.h>
  21.  
  22. #ifndef CMDPROMPT
  23. #define CMDPROMPT ">>> " /* Prompt user for immediate command */
  24. #endif CMDPROMPT
  25. #endif BTOP
  26.  
  27.  
  28. value editqueue();
  29.  
  30. /* Command line flags */
  31. extern bool dflag;
  32. extern bool slowterminal;
  33.  
  34. #ifdef SAVEBUF
  35. extern char copysavefile[];
  36. #endif SAVEBUF
  37.  
  38.  
  39. Visible bool lefttorite;
  40.     /* Saves some time in nosuggtoqueue() for read from file */
  41.  
  42. #define MAXHIST 101 /* One more than the number of UNDO's allowed. */
  43.  
  44. #define Mod(k) (((k)+MAXHIST) % MAXHIST)
  45. #define Succ(k) (((k)+1) % MAXHIST)
  46. #define Pred(k) (((k)+MAXHIST-1) % MAXHIST)
  47.  
  48. Hidden environ *tobesaved;
  49. Hidden string savewhere;
  50.  
  51.  
  52. #ifdef BTOP
  53.  
  54. extern jmp_buf jumpback;
  55. extern bool interrupted;
  56. extern bool canjump;
  57.  
  58. /*
  59.  * Main loop, called from main program if -t option present.
  60.  */
  61.  
  62. Visible Procedure
  63. mainloop()
  64. {
  65.     environ env;
  66.     environ *ep = &env;
  67.     FILE *pdown;
  68.     FILE *pup;
  69.     int cmdchar;
  70.  
  71.     savewhere = (string)NULL;
  72.     tobesaved = (environ*)NULL;
  73.     start_b(&pdown, &pup);
  74.     clrenv(ep);
  75. #ifdef SAVEBUF
  76.     ep->copybuffer = editqueue(copysavefile);
  77.     if (ep->copybuffer)
  78.         ep->copyflag = Yes;
  79. #endif SAVEBUF
  80.  
  81.     for (;;) {
  82.         cmdchar = sleur();
  83.         if (cmdchar == EOF)
  84.             break;
  85.         getinput(ep, cmdchar, pdown, pup);
  86.     }
  87. #ifdef SAVEBUF
  88.     if (ep->copyflag)
  89.         savequeue(ep->copybuffer, copysavefile);
  90.     else
  91.         savequeue(Vnil, copysavefile);
  92. #endif SAVEBUF
  93.     Erelease(*ep);
  94. }
  95.  
  96.  
  97. /*
  98.  * Provide input for the interpreter.
  99.  */
  100.  
  101. Hidden Procedure
  102. getinput(ep, cmdchar, pdown, pup)
  103.     environ *ep;
  104.     int cmdchar;
  105.     FILE *pdown;
  106.     FILE *pup;
  107. {
  108.     int n;
  109.     char buffer[100];
  110.     char filename[100];
  111.     int lineno;
  112.  
  113.  
  114.     switch (cmdchar) {
  115.  
  116.     case '>': /* Immediate command */
  117.     case 'E': /* Expression */
  118.     case 'R': /* Raw input */
  119.     case 'Y': /* Yes/No */
  120.         if (cmdchar == '>')
  121.             setroot("Imm_cmd");
  122.         else if (cmdchar == 'E')
  123.             setroot("Expression");
  124.         else
  125.             setroot("Raw_input");
  126.         delfocus(&ep->focus);
  127.         initshow();
  128.         if (cmdchar == '>')
  129.             cmdprompt(CMDPROMPT);
  130.         editdocument(ep);
  131.         endshow();
  132.         top(&ep->focus);
  133.         ep->mode = WHOLE;
  134.         if (!interrupted)
  135.             send(ep->focus, pdown);
  136.         delete(ep);
  137.         break;
  138.  
  139.     case ':':
  140.     case '=':
  141.         fgets(buffer, sizeof buffer, pup);
  142.         if (index(buffer, '+'))
  143.             n = sscanf(buffer, " +%d %s", &lineno, filename) - 1;
  144.         else {
  145.             n = sscanf(buffer, " %s", filename);
  146.             lineno = 0;
  147.         }
  148.         if (n == 1) {
  149.             initshow();
  150.             dofile(ep, filename, lineno);
  151.             endshow();
  152.             top(&ep->focus);
  153.             ep->mode = WHOLE;
  154.             delete(ep);
  155.             if (!ep->copyflag) {
  156.                 release(ep->copybuffer);
  157.                 ep->copybuffer = Vnil;
  158.             }
  159.         }
  160.         putc('\n', pdown);
  161.         interrupted = No; /* Interrupts handled locally in editdocument! */
  162.         break;
  163.  
  164.     default:
  165.         printf("[Unrecognized command character '%c' (0%o)]\n",
  166.             cmdchar&0177, cmdchar);
  167.  
  168.     }
  169. }
  170.  
  171. #endif BTOP
  172.  
  173.  
  174. #ifdef FILEARGS
  175.  
  176. /*
  177.  * Edit a single unit or target, called from main program if file name
  178.  * arguments are present.
  179.  */
  180.  
  181. Visible Procedure
  182. demo(filename, linenumber)
  183.     string filename;
  184.     int linenumber;
  185. {
  186.     environ env;
  187.     environ *ep = &env;
  188.     bool ok;
  189.  
  190.     clrenv(ep);
  191. #ifdef SAVEBUF
  192.     ep->copybuffer = editqueue(copysavefile);
  193.     if (ep->copybuffer)
  194.         ep->copyflag = Yes;
  195. #endif SAVEBUF
  196.     initshow();
  197.     ok = dofile(ep, filename, linenumber);
  198.     endshow();
  199.     if (!ok)
  200.         return No;
  201. #ifdef SAVEBUF
  202.     if (ep->copyflag)
  203.         savequeue(ep->copybuffer, copysavefile);
  204.     else
  205.         savequeue(Vnil, copysavefile);
  206. #endif SAVEBUF
  207.     Erelease(*ep);
  208.     return Yes;
  209. }
  210.  
  211. #endif !FILEARGS
  212.  
  213.  
  214. /*
  215.  * Edit a unit or target, using the environment offered as a parameter.
  216.  */
  217.  
  218. Hidden bool
  219. dofile(ep, filename, linenumber)
  220.     environ *ep;
  221.     string filename;
  222.     int linenumber;
  223. {
  224. #ifdef HELPFUL
  225.     static bool didmessage;
  226.  
  227.     if (!didmessage) {
  228.         didmessage = Yes;
  229.         message("[Press ? or ESC-? for help]");
  230.     }
  231. #endif HELPFUL
  232. #ifdef SAVEPOS
  233.     if (linenumber <= 0)
  234.         linenumber = getpos(filename);
  235. #endif SAVEPOS
  236.     setroot(filename[0] == '=' ? "Target_edit" : "Unit_edit");
  237.     savewhere = filename;
  238.     tobesaved = (environ*)NULL;
  239.  
  240.     lefttorite = Yes;
  241.     edit(ep, filename, linenumber);
  242. #ifdef USERSUGG
  243.     readsugg(ep->focus);
  244. #endif USERSUGG
  245.     lefttorite = No;
  246.  
  247.     ep->generation = 0;
  248.     if (!editdocument(ep))
  249.         return No;
  250.     if (ep->generation > 0) {
  251.         if (!save(ep->focus, filename))
  252.             error("Cannot save unit: %s", unixerror(filename));
  253. #ifdef USERSUGG
  254.         writesugg(ep->focus);
  255. #endif USERSUGG
  256.     }
  257. #ifdef SAVEPOS
  258.     savepos(filename, lineno(ep)+1);
  259. #endif SAVEPOS
  260.     savewhere = (char*)NULL;
  261.     tobesaved = (environ*)NULL;
  262.     return Yes;
  263. }
  264.  
  265.  
  266. /*
  267.  * Call the editor for a given document.
  268.  */
  269.  
  270. Hidden bool
  271. editdocument(ep)
  272.     environ *ep;
  273. {
  274.     int k;
  275.     int first = 0;
  276.     int last = 0;
  277.     int current = 0;
  278.     int onscreen = -1;
  279.     bool reverse = No;
  280.     environ newenv;
  281.     int cmd;
  282.     bool errors = No;
  283.     int undoage = 0;
  284.     bool done = No;
  285.     environ history[MAXHIST];
  286.  
  287.     Ecopy(*ep, history[0]);
  288.  
  289.     for (;;) { /* Command interpretation loop */
  290.         if (onscreen != current)
  291.             virtupdate(onscreen < 0 ? (environ*)NULL : &history[onscreen],
  292.                 &history[current],
  293.                 reverse && onscreen >= 0 ?
  294.                     history[onscreen].highest : history[current].highest);
  295.         onscreen = current;
  296.         if (done)
  297.             break;
  298. #ifdef BTOP
  299.         if (!interrupted && !moreinput())
  300. #else BTOP
  301.         if (!moreinput())
  302. #endif BTOP
  303.                 actupdate(history[current].copyflag ?
  304.                         history[current].copybuffer : Vnil,
  305. #ifdef RECORDING
  306.                     history[current].newmacro != Vnil,
  307. #else !RECORDING
  308.                     No,
  309. #endif !RECORDING
  310.                     No);
  311. #ifdef BTOP
  312.         if (interrupted || setjmp(jumpback))
  313.             break;
  314.         canjump = Yes;
  315. #endif BTOP
  316.         cmd = inchar();
  317. #ifdef BTOP
  318.         canjump = No;
  319. #endif BTOP
  320.         errors = No;
  321.  
  322.         switch (cmd) {
  323.  
  324. #ifndef NDEBUG
  325.         case Ctl(@): /* Debug exit with variable dump */
  326.             tobesaved = (environ*)NULL;
  327.             return No;
  328. #endif NDEBUG
  329.  
  330. #ifndef SMALLSYS
  331.         case Ctl(^): /* Debug status message */
  332.             dbmess(&history[current]);
  333.             errors = Yes; /* Causes clear after new keystroke seen */
  334.             continue;
  335. #endif !SMALLSYS
  336.  
  337.         case UNDO:
  338.             if (current == first)
  339.                 errors = Yes;
  340.             else {
  341.                 if (onscreen == current)
  342.                     reverse = Yes;
  343.                 current = Pred(current);
  344.                 undoage = Mod(last-current);
  345.             }
  346.             break;
  347.  
  348.         case REDO:
  349.             if (current == last)
  350.                 errors = Yes;
  351.             else {
  352.                 if (current == onscreen)
  353.                     reverse = No;
  354.                 if (history[Succ(current)].generation <
  355.                         history[current].generation)
  356.                     error(REDO_OLD); /***** Should refuse altogether??? *****/
  357.                 current = Succ(current);
  358.                 undoage = Mod(last-current);
  359.             }
  360.             break;
  361.  
  362. #ifdef HELPFUL
  363.         case HELP:
  364.             if (help())
  365.                 onscreen = -1;
  366.             break;
  367. #endif HELPFUL
  368.  
  369.         case REDRAW:
  370.             onscreen = -1;
  371.             trmundefined();
  372.             break;
  373.  
  374.         case EOF:
  375.             done = Yes;
  376.             break;
  377.  
  378.         default:
  379.             Ecopy(history[current], newenv);
  380.             newenv.highest = Maxintlet;
  381.             newenv.changed = No;
  382.             if (cmd != EXIT)
  383.                 errors = !execute(&newenv, cmd) || !checkep(&newenv);
  384.             else
  385.                 done = Yes;
  386.             if (errors) {
  387.                 switch (cmd) {
  388.                 case '\r':
  389.                 case '\n':
  390.                     if (newenv.mode == ATEND && !parent(newenv.focus)) {
  391.                         errors = !checkep(&newenv);
  392.                         if (!errors)
  393.                             done = Yes;
  394.                     }
  395.                     break;
  396. #ifdef HELPFUL
  397.                 case '?':
  398.                     if (help())
  399.                         onscreen = -1;
  400. #endif HELPFUL
  401.                 }
  402.             }
  403.             if (errors)
  404.                 Erelease(newenv);
  405.             else {
  406. #ifndef SMALLSYS
  407.                 if (done)
  408.                     done = canexit(&newenv);
  409. #endif SMALLSYS
  410.                 if (newenv.changed)
  411.                     ++newenv.generation;
  412.                 last = Succ(last);
  413.                 current = Succ(current);
  414.                 if (last == first) {
  415.                     /* Array full (always after a while). Discard "oldest". */
  416.                     if (current == last
  417.                         || undoage < Mod(current-first)) {
  418.                         Erelease(history[first]);
  419.                         first = Succ(first);
  420.                         if (undoage < MAXHIST)
  421.                             ++undoage;
  422.                     }
  423.                     else {
  424.                         last = Pred(last);
  425.                         Erelease(history[last]);
  426.                     }
  427.                 }
  428.                 if (current != last
  429.                     && newenv.highest < history[current].highest)
  430.                     history[current].highest = newenv.highest;
  431.                 /* Move entries beyond current one up. */
  432.                 for (k = last; k != current; k = Pred(k)) {
  433.                     if (Pred(k) == onscreen)
  434.                         onscreen = k;
  435.                     Emove(history[Pred(k)], history[k]);
  436.                 }
  437.                 Ecopy(newenv, history[current]);
  438.                 Erelease(history[current]);
  439.             }
  440.             break; /* default */
  441.  
  442.         } /* switch */
  443.  
  444.         if (errors && cmd != '?') {
  445.             if (!slowterminal && isascii(cmd)
  446.                 && (isprint(cmd) || cmd == ' '))
  447.                 error(INS_BAD, cmd);
  448.             else
  449.                 error((char*)NULL);
  450.         }
  451.         if (savewhere)
  452.             tobesaved = &history[current];
  453.     } /* for (;;) */
  454.  
  455.     actupdate(Vnil, No, Yes);
  456.     Erelease(*ep);
  457.     Ecopy(history[current], *ep);
  458.     if (savewhere)
  459.         tobesaved = ep;
  460.     for (current = first; current != last; current = Succ(current))
  461.         Erelease(history[current]);
  462.     Erelease(history[last]);
  463.     /* endshow(); */
  464.     return Yes;
  465. }
  466.  
  467.  
  468. /*
  469.  * Execute a command, return success or failure.
  470.  */
  471.  
  472. Hidden bool
  473. execute(ep, cmd)
  474.     register environ *ep;
  475.     register int cmd;
  476. {
  477.     register bool spflag = ep->spflag;
  478.     register int i;
  479.     environ env;
  480.     char buf[2];
  481.     register char *cp;
  482. #ifdef USERSUGG
  483.     bool sugg = symbol(tree(ep->focus)) == Suggestion;
  484. #define ACCSUGG(ep) if (sugg) accsugg(ep)
  485. #define KILLSUGG(ep) if (sugg) killsugg(ep)
  486. #else !USERSUGG
  487. #define ACCSUGG(ep) /* NULL */
  488. #define KILLSUGG(ep) /* NULL */
  489. #endif !USERSUGG
  490.  
  491. #ifdef RECORDING
  492.     if (ep->newmacro && cmd != USEMACRO && cmd != SAVEMACRO) {
  493.         buf[0] = cmd;
  494.         buf[1] = 0;
  495.         concato(&ep->newmacro, buf);
  496.     }
  497. #endif RECORDING
  498.     ep->spflag = No;
  499.  
  500.     switch (cmd) {
  501.  
  502. #ifdef RECORDING
  503.     case SAVEMACRO:
  504.         ep->spflag = spflag;
  505.         if (ep->newmacro) { /* End definition */
  506.             release(ep->oldmacro);
  507.             if (ep->newmacro && Length(ep->newmacro) > 0) {
  508.                 ep->oldmacro = ep->newmacro;
  509.                 message(REC_OK);
  510.             }
  511.             else {
  512.                 release(ep->newmacro);
  513.                 ep->oldmacro = Vnil;
  514.             }
  515.             ep->newmacro = Vnil;
  516.         }
  517.         else /* Start definition */
  518.             ep->newmacro = mk_text("");
  519.         return Yes;
  520.  
  521.     case USEMACRO:
  522.         if (!ep->oldmacro || Length(ep->oldmacro) <= 0) {
  523.             error(PLB_NOK);
  524.             return No;
  525.         }
  526.         ep->spflag = spflag;
  527.         cp = Str(ep->oldmacro);
  528.         for (i = 0; i < Length(ep->oldmacro); ++i) {
  529.             Ecopy(*ep, env);
  530.             if (execute(ep, cp[i]&0377) && checkep(ep))
  531.                 Erelease(env);
  532.             else {
  533.                 Erelease(*ep);
  534.                 Emove(env, *ep);
  535.                 if (!i)
  536.                     return No;
  537.                 error((char*)NULL); /* Just a bell */
  538.                 /* The error must be signalled here, because the overall
  539.                    command (USEMACRO) succeeds, so the main loop
  540.                    doesn't ring the bell; but we want to inform the
  541.                    that not everything was done either. */
  542.                 return Yes;
  543.             }
  544.         }
  545.         return Yes;
  546. #endif RECORDING
  547.  
  548. #ifndef SMALLSYS
  549.     case Ctl(_): /* 'Touch', i.e. set modified flag */
  550.         ep->changed = Yes;
  551.         return Yes;
  552. #endif SMALLSYS
  553.  
  554.     case GOTO:
  555.         ACCSUGG(ep);
  556. #ifdef RECORDING
  557.         if (ep->newmacro) {
  558.             error(GOTO_REC);
  559.             return No;
  560.         }
  561. #endif RECORDING
  562.         return gotocursor(ep);
  563.  
  564.     case NEXT:
  565.         ACCSUGG(ep);
  566.         return next(ep);
  567.  
  568.     case PREVIOUS:
  569.         ACCSUGG(ep);
  570.         return previous(ep);
  571.  
  572.     case LEFTARROW:
  573.         ACCSUGG(ep);
  574.         return leftarrow(ep);
  575.  
  576.     case RITEARROW:
  577.         ACCSUGG(ep);
  578.         return ritearrow(ep);
  579.  
  580.     case WIDEN:
  581.         ACCSUGG(ep);
  582.         return widen(ep);
  583.  
  584.     case EXTEND:
  585.         ACCSUGG(ep);
  586.         return extend(ep);
  587.  
  588.     case NARROW:
  589.         ACCSUGG(ep);
  590.         return narrow(ep);
  591.  
  592.     case RNARROW:
  593.         ACCSUGG(ep);
  594.         return rnarrow(ep);
  595.  
  596.     case UPARROW:
  597.         ACCSUGG(ep);
  598.         return uparrow(ep);
  599.  
  600.     case DOWNARROW:
  601.         ACCSUGG(ep);
  602.         return downarrow(ep);
  603.  
  604.     case UPLINE:
  605.         ACCSUGG(ep);
  606.         return upline(ep);
  607.  
  608.     case DOWNLINE:
  609.         ACCSUGG(ep);
  610.         return downline(ep);
  611.  
  612.     case COPY:
  613.         ACCSUGG(ep);
  614.         ep->spflag = spflag;
  615.         return copyinout(ep);
  616.  
  617.     case DELETE:
  618.         ACCSUGG(ep);
  619.         return delete(ep);
  620.  
  621.     case ACCEPT:
  622.         ACCSUGG(ep);
  623.         return accept(ep);
  624.  
  625.     default:
  626.         if (!isascii(cmd) || !isprint(cmd))
  627.             return No;
  628.         ep->spflag = spflag;
  629.         return ins_char(ep, cmd, islower(cmd) ? toupper(cmd) : -1);
  630.  
  631.     case ' ':
  632.         ep->spflag = spflag;
  633.         return ins_char(ep, ' ', -1);
  634.  
  635.     case RETURN:
  636.     case NEWLINE:
  637.         KILLSUGG(ep);
  638.         return ins_newline(ep);
  639.     }
  640. }
  641.  
  642.  
  643. /*
  644.  * Initialize an environment variable.  Most things are set to 0 or NULL.
  645.  */
  646.  
  647. Hidden Procedure
  648. clrenv(ep)
  649.     environ *ep;
  650. {
  651.     ep->focus = newpath(Pnil, gram(Optional), 1);
  652.     ep->mode = WHOLE;
  653.     ep->copyflag = ep->spflag = ep->changed = No;
  654.     ep->s1 = ep->s2 = ep->s3 = 0;
  655.     ep->highest = Maxintlet;
  656.     ep->copybuffer = Vnil;
  657. #ifdef RECORDING
  658.     ep->oldmacro = ep->newmacro = Vnil;
  659. #endif RECORDING
  660.     ep->generation = 0;
  661.     ep->changed = No;
  662. }
  663.  
  664.  
  665. /*
  666.  * Save parse tree and copy buffer.
  667.  */
  668.  
  669. Visible Procedure
  670. enddemo()
  671. {
  672.     register environ *ep = tobesaved;
  673.  
  674.     tobesaved = (environ*)NULL;
  675.         /* To avoid loops if saving is interrupted. */
  676.     if (savewhere && ep) {
  677.         if (ep->generation > 0) {
  678.             save(ep->focus, savewhere);
  679. #ifdef USERSUGG
  680.             writesugg(ep->focus);
  681. #endif USERSUGG
  682.         }
  683. #ifdef SAVEBUF
  684.         if (ep->copyflag)
  685.             savequeue(ep->copybuffer, copysavefile);
  686.         else
  687.             savequeue(Vnil, copysavefile);
  688. #endif SAVEBUF
  689. #ifdef SAVEPOS
  690.         savepos(savewhere, lineno(ep)+1);
  691. #endif SAVEPOS
  692.     }
  693. #ifdef BTOP
  694.     waitchild();
  695. #endif BTOP
  696. }
  697.  
  698.  
  699. /*
  700.  * Find out if the current position is higher in the tree
  701.  * than `ever' before (as remembered in ep->highest).
  702.  * The algorithm of pathlength() is repeated here to gain
  703.  * some efficiency by stopping as soon as it is clear
  704.  * no change can occur.
  705.  * (Higher() is called VERY often, so this pays).
  706.  */
  707.  
  708. Visible Procedure
  709. higher(ep)
  710.     register environ *ep;
  711. {
  712.     register path p = ep->focus;
  713.     register int pl = 0;
  714.     register int max = ep->highest;
  715.  
  716.     while (p) {
  717.         ++pl;
  718.         if (pl >= max)
  719.             return;
  720.         p = parent(p);
  721.     }
  722.     ep->highest = pl;
  723. }
  724.  
  725.  
  726. /*
  727.  * Issue debug status message.
  728.  */
  729.  
  730. Visible Procedure
  731. dbmess(ep)
  732.     register environ *ep;
  733. {
  734. #ifndef SMALLSYS
  735.     char stuff[80];
  736.     register string str = stuff;
  737.  
  738.     switch (ep->mode) {
  739.     case VHOLE:
  740.         sprintf(stuff, "VHOLE:%d.%d", ep->s1, ep->s2);
  741.         break;
  742.     case FHOLE:
  743.         sprintf(stuff, "FHOLE:%d.%d", ep->s1, ep->s2);
  744.         break;
  745.     case ATBEGIN:
  746.         str = "ATBEGIN";
  747.         break;
  748.     case ATEND:
  749.         str = "ATEND";
  750.         break;
  751.     case WHOLE:
  752.         str = "WHOLE";
  753.         break;
  754.     case SUBRANGE:
  755.         sprintf(stuff, "SUBRANGE:%d.%d-%d", ep->s1, ep->s2, ep->s3);
  756.         break;
  757.     case SUBSET:
  758.         sprintf(stuff, "SUBSET:%d-%d", ep->s1, ep->s2);
  759.         break;
  760.     case SUBLIST:
  761.         sprintf(stuff, "SUBLIST...%d", ep->s3);
  762.         break;
  763.     default:
  764.         sprintf(stuff, "UNKNOWN:%d,%d,%d,%d",
  765.             ep->mode, ep->s1, ep->s2, ep->s3);
  766.     }
  767.     message(
  768. #ifdef SAVEBUF
  769.         "%s, %s, wi=%d, hi=%d, (y,x,l)=(%d,%d,%d) %s",
  770.         symname(symbol(tree(ep->focus))),
  771. #else !SAVEBUF
  772.         "%d, %s, wi=%d, hi=%d, (y,x,l)=(%d,%d,%d) %s",
  773.         symbol(tree(ep->focus)),
  774. #endif SAVEBUF
  775.         str, width(tree(ep->focus)), ep->highest,
  776.         Ycoord(ep->focus), Xcoord(ep->focus), Level(ep->focus),
  777.             ep->spflag ? "spflag on" : "");
  778. #endif !SMALLSYS
  779. }
  780.  
  781. #ifndef SMALLSYS
  782.  
  783. Hidden bool
  784. canexit(ep)
  785.     environ *ep;
  786. {
  787.     environ env;
  788.  
  789.     shrink(ep);
  790.     if (ishole(ep))
  791.         delete(ep);
  792.     Ecopy(*ep, env);
  793.     top(&ep->focus);
  794.     higher(ep);
  795.     ep->mode = WHOLE;
  796.     if (findhole(&ep->focus)) {
  797.         Erelease(env);
  798.         error(EXIT_HOLES); /* There are holes left */
  799.         return No;
  800.     }
  801.     Erelease(*ep);
  802.     Emove(env, *ep);
  803.     return Yes;
  804. }
  805.  
  806.  
  807. Hidden bool
  808. findhole(pp)
  809.     register path *pp;
  810. {
  811.     register node n = tree(*pp);
  812.  
  813.     if (Type(n) == Tex)
  814.         return No;
  815.     if (symbol(n) == Hole)
  816.         return Yes;
  817.     if (!down(pp))
  818.         return No;
  819.     for (;;) {
  820.         if (findhole(pp))
  821.             return Yes;
  822.         if (!rite(pp))
  823.             break;
  824.  
  825.     }
  826.     up(pp) || Abort();
  827.     return No;
  828. }
  829.  
  830. #endif !SMALLSYS
  831.